import * as React from 'react';
import { Input, InputProps } from '@/components/ui/input.tsx';
import { getNumber } from '@/utils/base.ts';
import { useEffect, useMemo, useState } from 'react';
import { cn } from '@/components/ui/lib/utils.ts';

export interface NumberInputProps extends InputProps {
    value: number;
    max?: number;
    min?: number;
    onValueChange: (value: number) => void;
    acceptNegative?: boolean;
    acceptNaN?: boolean;
}

const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
    ({ className, type, ...props }, ref) => {
        const [value, setValue] = useState(props.value.toString());
        useEffect(() => {
            // fix life cycle: update value when props.value changed
            if (getValue(value.toString()) !== props.value) {
                setValue(props.value.toString());
            }
        }, [props.value]);

        const getValue = (v: string) => {
            const raw = getNumber(v, props.acceptNegative);
            let val = parseFloat(raw);
            if (isNaN(val) && !props.acceptNaN) val = 0;
            if (props.max !== undefined && val > props.max) val = props.max;
            else if (props.min !== undefined && val < props.min)
                val = props.min;
            return val;
        };

        const formatValue = (v: string) => {
            if (v.trim().length === 0) return v.trim();

            if (!/^[-+]?(?:[0-9]*(?:\.[0-9]*)?)?$/.test(v)) {
                const exp = /[-+]?[0-9]+(\.[0-9]+)?/g;
                return v.match(exp)?.join('') || '';
            }

            if (v === '-' && props.acceptNegative) return v;

            // replace -0124.5 to -124.5, 0043 to 43, 2.000 to 2.000
            const exp = /^[-+]?0+(?=[0-9]+(\.[0-9]+)?$)/;
            v = v.replace(exp, '');

            const raw = getNumber(v, props.acceptNegative);
            let val = parseFloat(raw);
            if (isNaN(val) && !props.acceptNaN)
                return (props.min ?? 0).toString();
            if (props.max !== undefined && val > props.max)
                return props.max.toString();
            else if (props.min !== undefined && val < props.min)
                return props.min.toString();

            return v;
        };

        const isValid = useMemo((): boolean => {
            if (!/^[-+]?[0-9]+(\.[0-9]+)?$/.test(value)) return false;
            const val = getValue(value);
            if (props.max !== undefined && val > props.max) return false;
            else if (props.min !== undefined && val < props.min) return false;
            return true;
        }, [value]);

        return (
            <Input
                ref={ref}
                className={cn(
                    'number-input transition',
                    className,
                    !isValid && 'border-red-600 focus:border-red-700'
                )}
                id={props.id}
                value={value}
                onChange={e => {
                    setValue(formatValue(e.target.value));
                    props.onValueChange(getValue(e.target.value));
                }}
                min={props.min}
                max={props.max}
                onWheel={e => {
                    e.stopPropagation();
                }}
            />
        );
    }
);
NumberInput.displayName = 'NumberInput';

export { NumberInput };
